You Don't Know JS上卷Part1 Chapter4.提升

第4章 提升

  • 任何声明在某个作用域内的变量,都将附属于这个作用域。
  • 包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
  • var a = 2;会被看成两个声明,var a;a = 2;,第一个声明在编译阶段进行,第二个赋值声明会被留在原地等待执行阶段
  • 所有的声明(变量和函数)都会被“移动”到各自作用域的最顶端,这个过程叫做提升
  • 只有声明本身会被提升,而包括函数表达式在内的赋值或其他运行逻辑并不会提升。
1
2
3
4
5
6
7
8
9
10
11
12
13
a = 2;

var a;

console.log( a ); // 2

---------------------------------------
// 实际按如下形式进行处理
var a; // 编译阶段

a = 2; // 执行阶段

console.log( a ); // 2
1
2
3
4
5
6
7
8
9
10
11
console.log( a ); // undefinde

var a = 2;

---------------------------------------
// 实际按如下形式进行处理
var a; // 编译

console.log( a ); // undefinde

a = 2; // 执行
  • 每个作用域都会进行变量提升
1
2
3
4
5
6
7
8
9
function foo() {
var a;

console.log( a ); // undefinde

a = 2;
}

foo();
  • 函数声明会被提升,但是函数表达式不会被提升
1
2
3
4
5
foo(); // 不是ReferenceError,而是TypeError!

var foo = function bar() {
// ...
};

上面这段程序中,变量标识符foo()被提升并分配给所在作用域,因此foo()不会导致ReferenceError。此时foo并没有赋值(如果它是一个函数声明而不是函数表达式,那么就会赋值),foo()由于对undefined值进行函数调用而导致非法操作,因此抛出TypeError异常。

  • 即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
foo(); // TypeError
bar(); // ReferenceError

var foo = function bar() {
// ...
};

---------------------------------------
// 实际按如下形式进行处理
var foo;

foo(); // TypeError
bar(); // ReferenceError

foo = function() {
var bar = ...self...
// ...
};

4.1 函数优先

  • 函数声明和变量声明都会被提升,但是,函数首先被提升,然后才是变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
foo(); // 1

var foo;

function foo() {
console.log( 1 );
};

foo = function() {
console.log( 2 );
};

---------------------------------------
// 实际按如下形式进行处理

function foo() { // 函数提升是整体提升,声明 + 赋值
console.log( 1 );
};

foo(); // 1

foo = function() {
console.log( 2 );
};
  • var foo尽管出现在function foo()...的声明之前,但它是重复的声明,且函数声明会被提升到普通变量之前,因此被忽略
  • 后面出现的函数声明可以覆盖前面的。
1
2
3
4
5
6
7
8
9
10
11
12
13
foo(); // 3

function foo() {
console.log( 1 );
};

var foo = function() {
console.log( 2 );
};

function foo() {
console.log( 3 );
};
  • 一个普通块内部的函数声明通常会被提升到所在作用域的顶部,不会被条件判断所控制。尽量避免在普通块内部声明函数
1
2
3
4
5
6
7
8
9
foo(); // "b"

var a = true;
if (a) {
function foo() { console.log( "a" ); };
}
else {
function foo() { console.log( "b" ); };
}